#pragma once

#include <iostream>
#include <string>
#include <vector>
#include "reactor.hpp"
#include "util.hpp"

namespace ns_appinterface{

    using namespace ns_epoll;

    int recver(EventItem *item);
    int sender(EventItem *item);
    int errorer(EventItem *item);

    //请问，什么时候会调用这里的accepter?
    int accepter(EventItem *item)
    {
        std::cout << "get a new link: " << item->sock << std::endl;
        while(true){
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sock = accept(item->sock, (struct sockaddr*)&peer, &len);
            if(sock < 0){
                if(errno == EAGAIN || errno == EWOULDBLOCK){
                    //说明并没有读取出错，只是底层没有链接了!
                    return 0;
                }
                if(errno == EINTR){
                    //读取的过程被信号打断了
                    continue;
                }
                else{
                    //真正的出错了
                    //item->error_handler(item);
                    return -1;
                }
            }
            else{
                //立即将fd设置为非阻塞模式
                ns_util::SetNonBlock(sock);
                //读取成功了!
                EventItem tmp;
                tmp.sock = sock;
                tmp.R = item->R;
                tmp.ManagerCallBack(recver, sender, errorer);
                Reactor *epoller = item->R;
                //epoll,经常一定会设置读事件就绪，而写事件我们是按需打开！
                epoller->AddEvent(sock, EPOLLIN|EPOLLET, tmp);

            }
        }
        return 0;
    }

    //0:读取成功
    //-1:读取失败
    int recver_helper(int sock, std::string *out)
    {
        while(true){
            char buffer[128];
            ssize_t size = recv(sock, buffer, sizeof(buffer)-1, 0);
            if(size < 0){
                if( errno == EAGAIN || errno == EWOULDBLOCK ){
                    //循环读取，读取完毕了
                    return 0;
                }
                else if( errno == EINTR ){
                    //被信号中断，就继续尝试
                    continue;
                }
                else{
                    //真正读取出错了, TODO
                    return -1;
                }
            }
            else{
                buffer[size] = 0; //string
                *out += buffer; //将我们读取到的内容，无脑添加到inbuffer中!
            }
        }
    }

    int recver(EventItem *item)
    {
        std::cout << "recv event ready: " << item->sock << std::endl;
        //负责数据读取
        //1. 需要整体读，非阻塞
        if( recver_helper(item->sock, &(item->inbuffer)) < 0 ) //读取失败
        {
            //item->error_handler
            //item->error_handler(item);
            return -1;
        }
        std::cout <<"client# " << item->inbuffer << std::endl;
        //2. 根据发来的数据流，进行包和包之间的分离，防止粘包问题, 这里是涉及到协议定制呢！！！\n X
        std::vector<std::string> messages;
        ns_util::StringUtil::Split(item->inbuffer, &messages, "X");
        //for(auto s : messages){
        //    std::cout <<"#################################" << std::endl;
        //    std::cout << "提取出:" << s << std::endl;
        //    std::cout <<"#################################" << std::endl;
        //}
        //std::cout <<" 剩余 " << item->inbuffer << std::endl;
        
        //3. 针对一个一个的报文协议反序列化decode, 也是协议定制的一部分
        //1+1X2+2X3%5X
        //1+1 2+2 3%5 
        //1,+, 1, struct op{int x, int y, char op} -> "1+1", json xml
        //"1+2" -> struct data{int x, int y};
        struct data{
            int x;
            int y;
        };
        //已经拿到了所有的数据报文 
        for(auto s : messages){
            
            struct data d;
            ns_util::StringUtil::Deserialize(s, &d.x, &d.y);

            //Reactor如果只负责读取数据流，进行报文和报文的分离，不在进行后续处理，把后续的业务处理，形成报文，构建响应等这个工作
            //交给后端软件层或者线程进程池，基于Reactor的半同步半异步的工作方式, 是Linux中最常用的工作方式，几乎没有之一
            //task t(d);
            //thread_pool->push(t);

            //std::cout << d.x << " : " << d.y << std::endl;
            //4. 业务处理
            int z = d.x + d.y;

            //5. 形成响应报文，序列化转化成为一个字符串encode
            std::string response;
            response += std::to_string(d.x);
            response += "+";
            response += std::to_string(d.y);
            response += "=";
            response += std::to_string(z);

            item->outbuffer += response;
            //5.1 设置响应报文和响应报文之间的分隔符
            item->outbuffer += "X"; //encode
        }
        //6. 写回
        if(!item->outbuffer.empty()) item->R->EnableReadWrite(item->sock, true, true);
        return 0;
    }

    //0: 写完inbuffer
    //1：缓冲区打满，下次写入
    //-1:写出错
    int sender_helper(int sock, std::string &in)
    {
        size_t total = 0;
        while(true){
            ssize_t s = send(sock, in.c_str()+total, in.size()-total, 0);
            if(s > 0){
                total += s;
                if(total >= in.size()){
                    return 0;
                }
            }
            else if(s < 0){
                if(errno == EAGAIN || errno == EWOULDBLOCK){
                    //无论是否发送完，inbuffer，都需要将已经发送的数据，全部移出缓冲区
                    in.erase(total);
                    return 1; //已经将缓冲区打满，不能在写入了，但是有没有写完？
                }
                else if(errno == EINTR){
                    continue;
                }
                else{
                    //TO DO
                    return -1;
                }
            }
        }//end of while
    }

    int sender(EventItem *item)
    {
        int ret = sender_helper(item->sock, item->outbuffer);
        if(ret == 0){
            item->R->EnableReadWrite(item->sock, true, false);
        }
        else if(ret == 1){
            //默认不是已经打开了吗？
            //一般我们只要设置了EPOLLOUT，默认epoll会在下次自动触发一次
            item->R->EnableReadWrite(item->sock, true, true);
        }
        else{
            //TO DO
            //item->error_handler(item);
        }
        return 0;
    }
   
    //可能会有bug！
    int errorer(EventItem *item)
    {
        close(item->sock);
        item->R->DelEvent(item->sock);
        return 0;
    }
}
